<?php
/**
 * 入口文件应用管理类
 * 
 * Copyright (c) 2014~2019 https://desion.net All rights reserved.
 * Author: 阿林 QQ:120867629 <linhong00@gmail.com>
 */
declare (strict_types = 1);
namespace app\controller;

// 模版类
class Template
{
    public $themePath = '/';
    // 模板变量
    protected $data = [];
    // 引擎配置
    protected $config = [
        'tpl_suffix'         => '.html', // 默认模板文件后缀
        'cache_suffix'       => '.php', // 默认模板缓存后缀
        'tpl_cache'          => true, // 是否开启模板编译缓存,设为false则每次都会重新编译
        'cache_time'         => 0, // 模板缓存有效期 0 为永久，(以数字为值，单位:秒)
        'tpl_begin'          => '{', // 模板引擎普通标签开始标记
        'tpl_end'            => '}', // 模板引擎普通标签结束标记
        'strip_space'        => false, // 是否去除模板文件里面的html空格与换行
        'cache_path'         => 'theme',
        'theme_name'         => 'default',
        'debug'              => false,
    ];
    // 标签定义
    protected $tags = [
        // 标签定义： attr 属性列表 close 是否闭合（0 或者1 默认1） level 嵌套层次
        'foreach'    => ['attr' => 'name,id,item,key,offset,length,mod', 'expression' => true],
        'if'         => ['attr' => 'condition', 'expression' => true],
        'elseif'     => ['attr' => 'condition', 'close' => 0, 'expression' => true],
        'else'       => ['attr' => '', 'close' => 0],
        'switch'     => ['attr' => 'name', 'expression' => true],
        'case'       => ['attr' => 'value,break', 'expression' => true],
        'default'    => ['attr' => '', 'close' => 0]
    ];
    // 表达式
    protected $comparison = [' nheq ' => ' !== ', ' heq ' => ' === ', ' neq ' => ' != ', ' eq ' => ' == ', ' egt ' => ' >= ', ' gt ' => ' > ', ' elt ' => ' <= ', ' lt ' => ' < '];


    /**
     * 架构函数
     */
    public function __construct(array $config = [])
    {
        if ($config['theme_path']) {
            $this->themePath = $config['theme_path'];
        }

        if ($config) {
            $this->config = array_merge($this->config, $config);
        }
    }

    /**
     * 模板变量赋值
     *
     * @param  mixed $name
     * @param  mixed $value
     * @return viod
     */
    public function assign($name, $value = '')
    {
        if (is_array($name)) {
            $this->data = array_merge($this->data, $name);
        } else {
            $this->data[$name] = $value;
        }
    }

    /**
     * 渲染模板文件
     *
     * @param string $template 模板文件
     * @param array  $vars 模板变量
     * @return void
     */
    public function display($template, $vars = [])
    {
        // 合并模板变量
        if ($vars) {
            $this->data = array_merge($this->data, $vars);
        }
        // 读取缓存
        $cacheFile = $this->config['cache_path'] . $this->config['theme_name'] . '_' . str_replace('\/', '_',$template) . $this->config['cache_suffix'];

        if ($this->config['debug'] === true || ! $this->_checkCache($cacheFile)) {
            $content = $this->_parseTemplate($template, true);
            $this->_compiler($content, $cacheFile);
        }
        // 页面缓存
        ob_start();
        ob_implicit_flush(0);
        // 读取编译存储
        $this->_read($cacheFile, $this->data);
        // 获取并清空缓存
        return ob_get_clean();
    }

    /**
     * 自动定位模板文件
     *
     * @param  string  $template  模板文件规则
     * @return string
     */
    private function _parseTemplate($template, $getContent = false)
    {
        $theme    = $this->_setPathList();
        $template = $this->themePath . $theme . $template . $this->config['tpl_suffix'];

        if (! is_file($template)) {
            throw new \Exception('template file not exists :' . $template, 10201);
        }

        if ($getContent) {
            $template = file_get_contents($template);
        }

        return $template;
    }
    // 组合路径
    private function _setPathList()
    {
        $path = $this->config['theme_name'] . DIRECTORY_SEPARATOR;
        $theme = '/static/' . $this->config['theme_name'] . '/';

        $list = [
            '__CSS__'   => $theme . 'css/',
            '__JS__'    => $theme . 'js/',
            '__THEME__' => $theme
        ];

        $this->data['desion'] = $list;

        return $path;
    }

    /**
     * 编译模板文件内容
     *
     * @param  string $content   模板内容
     * @param  string $cacheFile 缓存文件名
     * @return void
     */
    private function _compiler(& $content, $cacheFile)
    {
        // 模板解析
        $this->parse($content);
        // 添加安全代码
        $content = '<?php if (!defined(\'DSPHP_PATH\')) exit();?>' . $content;
        // 去除html空格与换行
        if ($this->config['strip_space']) {
            $find    = ['~>\s+<~', '~>(\s+\n|\r)~'];
            $replace = ['><', '>'];
            $content = preg_replace($find, $replace, $content);
        }
        // 优化生成的php代码
        $content = preg_replace('/\?>\s*<\?php\s?/is', '', $content);
        // 编译存储
        $this->_write($cacheFile, $content);
        return;
    }

    /**
     * 模板解析入口
     *
     * @param  string $content 要解析的模板内容
     * @return viod
     */
    public function parse(&$content)
    {
        // 内容为空不解析
        if (empty($content)) {
            return ;
        }
        // 检查include语法
        $this->_parseInclude($content);
        // 检查PHP语法
        $this->_parsePhp($content);
        // 解析模板标签 if foreach
        $this->_parseTagLib($content);
        // 解析普通模板标签{$tagName}
        $this->_parseTag($content);

        return;
    }

    /**
     * 解析模板中的include标签
     *
     * @param  string $content 要解析的模板内容
     * @return void
     */
    private function _parseInclude(& $content)
    {
        $end   = $this->config['tpl_end'];
        $regex = '/' . $this->config['tpl_begin'] . 'include\b(?>(?:(?!file=).)*)\bfile=([\'\"])(?<name>[\$\w\-\/\.\:@,\\\\]+)\\1(?>[^' . $end . ']*)' . $end . '/is';

        $funReplace = function ($template) use (& $funReplace, & $regex, & $content) {
            if (preg_match_all($regex, $template, $matches, PREG_SET_ORDER)) {
                foreach ($matches as $match) {
                    $array = $this->parseAttr($match[0]);
                    $file  = $array['file'];
                    unset($array['file']);
                    // 分析模板文件名并读取内容
                    $parseStr = $this->_parseTemplate($file, true);
                    // 替换变量
                    foreach ($array as $k => $v) {
                        $parseStr = str_replace('[' . $k . ']', $v, $parseStr);
                    }
                    // 再次对包含文件进行模板分析
                    $funReplace($parseStr);
                    $content = str_replace($match[0], $parseStr, $content);
                }

                unset($matches);
            }
        };
        // 替换模板中的include标签
        $funReplace($content);
    }

    /**
     * 检查PHP语法
     *
     * @param string $content 要解析的模板内容
     * @return void
     */
    private function _parsePhp(& $content)
    {
        $content = preg_replace('/(<\?(?!php|=|$))/i', '<?php echo \'\\1\'; ?>' . "\n", $content);
    }

    /**
     * 分析标签属性
     *
     * @param  string $str 属性字符串
     * @param  string $name 不为空时返回指定的属性名
     * @return array
     */
    public function parseAttr($str, $name = null)
    {
        $regex = '/\s+(?>(?<name>\w+)\s*)=(?>\s*)([\"\'])(?<value>(?:(?!\\2).)*)\\2/is';
        $array = [];

        if (preg_match_all($regex, $str, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $array[$match['name']] = $match['value'];
            }
            unset($matches);
        }

        if (! empty($name) && isset($array[$name])) {
            return $array[$name];
        } else {
            return $array;
        }
    }

    /**
     * 模板标签解析
     * 格式： {TagName:args [|content] }
     * @access private
     * @param  string $content 要解析的模板内容
     * @return void
     */
    private function _parseTag(& $content)
    {
        $begin = $this->config['tpl_begin'];
        $end   = $this->config['tpl_end'];
        $regex = '/' . $begin . '((?:[\$]{1,2}[a-wA-w_]|[\:\~][\$a-wA-w_]|[+]{2}[\$][a-wA-w_]|[-]{2}[\$][a-wA-w_]|\/[\*\/])(?>(?:(?!' . $end . ').)*))' . $end . '/is';

        if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
            foreach ($matches as $match) {
                $str  = stripslashes($match[1]);
                $flag = substr($str, 0, 1);
                switch ($flag) {
                    case ':':
                        // 输出某个函数的结果
                        $str = substr($str, 1);
                        $this->parseVar($str);
                        $str = '<?php echo ' . $str . '; ?>';
                        break;
                    case '$':
                    case '-':
                    case '+':
                        // 输出计算
                        $this->parseVar($str);
                        $str = '<?php echo ' . $str . '; ?>';
                        break;
                    default:
                        // 未识别的标签直接返回
                        $str = $begin . $str . $end;
                        break;
                }
                $content = str_replace($match[0], $str, $content);
            }
            unset($matches);
        }
        return;
    }

    /**
     * 模板变量解析,支持使用函数
     * 格式： {$varname|function1|function2=arg1,arg2}
     *
     * @param  string $varStr 变量数据
     * @return void
     */
    public function parseVar(&$varStr)
    {
        $varStr = trim($varStr);
        if (preg_match_all('/\$[a-zA-Z_](?>\w*)(?:[:\.][0-9a-zA-Z_](?>\w*))+/', $varStr, $matches, PREG_OFFSET_CAPTURE)) {
            static $_varParseList = [];
            while ($matches[0]) {
                $match = array_pop($matches[0]);
                //如果已经解析过该变量字串，则直接返回变量值
                if (isset($_varParseList[$match[0]])) {
                    $parseStr = $_varParseList[$match[0]];
                } else {
                    if (strpos($match[0], '.')) {
                        $vars  = explode('.', $match[0]);
                        $first = array_shift($vars);
                        if ('$Desion' == $first) {
                            $parseStr = $this->parseDesionVar($vars);
                        } else {
                            // 识别为数组
                            $parseStr = $first . '[\'' . implode('\'][\'', $vars) . '\']';
                        }
                    }

                    $_varParseList[$match[0]] = $parseStr;
                }
                $varStr = substr_replace($varStr, $parseStr, $match[1], strlen($match[0]));
            }
            unset($matches);
        }
        return;
    }

    // 检查编译缓存是否有效
    private function _checkCache($cacheFile)
    {
        if (! $this->config['tpl_cache']) {
            return false;
        }
        // 文件不存在
        if (! is_file($cacheFile)) {
            return false;
        }
        // 读取缓存文件失败
        if (@fopen($cacheFile, "r")){
            return false;
        }

        $cacheTime = $this->config['cache_time'];

        if (0 != $cacheTime && time() > filemtime($cacheFile) + $cacheTime) {
            // 缓存是否在有效期
            return false;
        }

        return true;
    }

    /**
     * 特殊模板变量解析
     * 格式 以 $Desion. 打头的变量属于特殊模板变量
     *
     * @param  array $vars 变量数组
     * @return string
     */
    public function parseDesionVar(&$vars)
    {
        $vars[0]  = strtoupper(trim($vars[0]));
        $parseStr = '';
        if (count($vars) >= 2) {
            $vars[1] = trim($vars[1]);
            switch ($vars[0]) {
                case 'SERVER':
                    $parseStr = '$_SERVER[\'' . strtoupper($vars[1]) . '\']';
                    break;
                case 'GET':
                    $parseStr = '$_GET[\'' . $vars[1] . '\']';
                    break;
                case 'POST':
                    $parseStr = '$_POST[\'' . $vars[1] . '\']';
                    break;
                case 'COOKIE':
                    if (isset($vars[2])) {
                        $parseStr = '$_COOKIE[\'' . $vars[1] . '\'][\'' . $vars[2] . '\']';
                    } else {
                        $parseStr = '\\Desion\\Cookie::get(\'' . $vars[1] . '\')';
                    }
                    break;
                case 'SESSION':
                    if (isset($vars[2])) {
                        $parseStr = '$_SESSION[\'' . $vars[1] . '\'][\'' . $vars[2] . '\']';
                    } else {
                        $parseStr = '\\desion\\Session::get(\'' . $vars[1] . '\')';
                    }
                    break;
                case 'REQUEST':
                    $parseStr = '$_REQUEST[\'' . $vars[1] . '\']';
                    break;
                case 'CONST':
                    $parseStr = strtoupper($vars[1]);
                    break;
                case 'DESION':
                    $parseStr = '\''.$this->data['desion'][$vars[1]].'\'';
                    break;
                default:
                    $parseStr = '\'\'';
                    break;
            }
        } else {
            if (count($vars) == 1) {
                switch ($vars[0]) {
                    case 'NOW':
                        $parseStr = "date('Y-m-d g:i a',time())";
                        break;
                    case 'VERSION':
                        $parseStr = 'DSPHP_VERSION';
                        break;
                    default:
                        if (defined($vars[0])) {
                            $parseStr = $vars[0];
                        }
                }
            }
        }
        return $parseStr;
    }

    public function _parseTagLib(& $content)
    {
        $tags = [];
        $lib  = '';
        foreach ($this->tags as $name => $val) {
            $close               = !isset($val['close']) || $val['close'] ? 1 : 0;
            $tags[$close][$name] = $name;
        }

        // 闭合标签
        if (! empty($tags[1])) {
            $nodes = [];
            $regex = $this->getRegex(array_keys($tags[1]), 1);
            if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
                $right = [];
                foreach ($matches as $match) {
                    if ('' == $match[1][0]) {
                        $name = strtolower($match[2][0]);
                        // 如果有没闭合的标签头则取出最后一个
                        if (!empty($right[$name])) {
                            // $match[0][1]为标签结束符在模板中的位置
                            $nodes[$match[0][1]] = [
                                'name'  => $name,
                                'begin' => array_pop($right[$name]), // 标签开始符
                                'end'   => $match[0], // 标签结束符
                            ];
                        }
                    } else {
                        // 标签头压入栈
                        $right[strtolower($match[1][0])][] = $match[0];
                    }
                }
                unset($right, $matches);
                // 按标签在模板中的位置从后向前排序
                krsort($nodes);
            }

            $break = '<!--###break###--!>';
            if ($nodes) {
                $beginArray = [];
                // 标签替换 从后向前
                foreach ($nodes as $pos => $node) {
                    // 对应的标签名
                    $name  = $tags[1][$node['name']];
                    $alias = $name != $node['name'] ? $node['name'] : '';
                    // 解析标签属性
                    $attrs  = $this->parseAttr($node['begin'][0], $name, $alias);
                    $method = 'tag' . $name;
                    // 读取标签库中对应的标签内容 replace[0]用来替换标签头，replace[1]用来替换标签尾
                    $replace = explode($break, $this->$method($attrs, $break));
                    if (count($replace) > 1) {
                        while ($beginArray) {
                            $begin = end($beginArray);
                            // 判断当前标签尾的位置是否在栈中最后一个标签头的后面，是则为子标签
                            if ($node['end'][1] > $begin['pos']) {
                                break;
                            } else {
                                // 不为子标签时，取出栈中最后一个标签头
                                $begin = array_pop($beginArray);
                                // 替换标签头部
                                $content = substr_replace($content, $begin['str'], $begin['pos'], $begin['len']);
                            }
                        }
                        // 替换标签尾部
                        $content = substr_replace($content, $replace[1], $node['end'][1], strlen($node['end'][0]));
                        // 把标签头压入栈
                        $beginArray[] = ['pos' => $node['begin'][1], 'len' => strlen($node['begin'][0]), 'str' => $replace[0]];
                    }
                }

                while ($beginArray) {
                    $begin = array_pop($beginArray);
                    // 替换标签头部
                    $content = substr_replace($content, $begin['str'], $begin['pos'], $begin['len']);
                }
            }
        }
        // 自闭合标签
        if (! empty($tags[0]))
        {
            $regex   = $this->getRegex(array_keys($tags[0]), 0);
            $content = preg_replace_callback($regex, function ($matches) use (&$tags, &$lib) {
                // 对应的标签名
                $name  = $tags[0][strtolower($matches[1])];
                $alias = $name != $matches[1] ? $matches[1] : '';
                // 解析标签属性
                $attrs  = $this->parseAttr($matches[0], $name, $alias);
                $method = 'tag' . $name;
                return $this->$method($attrs, '');
            }, $content);
        }
    }

    /**
     * 按标签生成正则
     * @access private
     * @param  array|string     $tags 标签名
     * @param  boolean          $close 是否为闭合标签
     * @return string
     */
    private function getRegex($tags, $close)
    {
        $begin   = $this->config['tpl_begin'];
        $end     = $this->config['tpl_end'];

        $tagName = is_array($tags) ? implode('|', $tags) : $tags;

        if ($close) {
            // 如果是闭合标签
            $regex = $begin . '(?:(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)|\/(' . $tagName . '))' . $end;
        } else {
            $regex = $begin . '(' . $tagName . ')\b(?>(?:(?!' . $end . ').)*)' . $end;
        }

        return '/' . $regex . '/is';
    }

    /**
     * 写入编译缓存
     *
     * @param  string  $cacheFile   文件地址
     * @param  string  $content     内容
     * @return void
     */
    private function _write($cacheFile, $content)
    {
        // 检测模板目录
        $dir = dirname($cacheFile);
        if (! is_dir($dir)) {
            mkdir($dir, 0777, true);
        }
        // 生成模板缓存文件
        if (false === file_put_contents($cacheFile, $content)) {
            throw new \Exception('cache write error :' . $cacheFile, 10202);
        }
    }

    /**
     * 读取编译编译
     *
     * @param  string $cacheFile   文件地址
     * @param  array  $vars        变量
     * @return string
     */
    private function _read($cacheFile, $vars)
    {
        // 模板阵列变量分解成为独立变量
        extract($vars, EXTR_OVERWRITE);
        //载入模版缓存文件
        include $cacheFile;
    }

    /**
     * foreach标签解析 循环输出数据集
     * 格式：
     * {foreach name="userList" id="user" key="key" index="i" mod="2" offset="3" length="5" empty=""}
     * {user.username}
     * {/foreach}
     * @access public
     * @param array $tag 标签属性
     * @param string $content 标签内容
     * @return string|void
     */
    public function tagForeach($tag, $content)
    {
        // 直接使用表达式
        if (!empty($tag['expression'])) {
            $expression = ltrim(rtrim($tag['expression'], ')'), '(');
            $expression = $this->autoBuildVar($expression);
            $parseStr   = '<?php foreach(' . $expression . '): ?>';
            $parseStr .= $content;
            $parseStr .= '<?php endforeach; ?>';
            return $parseStr;
        }
        $name   = $tag['name'];
        $key    = !empty($tag['key']) ? $tag['key'] : 'key';
        $item   = !empty($tag['id']) ? $tag['id'] : $tag['item'];
        $empty  = isset($tag['empty']) ? $tag['empty'] : '';
        $offset = !empty($tag['offset']) && is_numeric($tag['offset']) ? intval($tag['offset']) : 0;
        $length = !empty($tag['length']) && is_numeric($tag['length']) ? intval($tag['length']) : 'null';

        $parseStr = '<?php ';
        // 支持用函数传数组
        if (':' == substr($name, 0, 1)) {
            $var  = '$_' . uniqid();
            $name = $this->autoBuildVar($name);
            $parseStr .= $var . '=' . $name . '; ';
            $name = $var;
        } else {
            $name = $this->autoBuildVar($name);
        }
        $parseStr .= 'if(is_array(' . $name . ')): ';
        // 设置了输出数组长度
        if (0 != $offset || 'null' != $length) {
            if (!isset($var)) {
                $var = '$_' . uniqid();
            }
            $parseStr .= $var . ' = is_array(' . $name . ') ? array_slice(' . $name . ',' . $offset . ',' . $length . ', true) : ' . $name . '->slice(' . $offset . ',' . $length . ', true); ';
        } else {
            $var = &$name;
        }

        $parseStr .= 'if( count(' . $var . ')==0 ) : echo "' . $empty . '" ;';
        $parseStr .= 'else: ';

        // 设置了索引项
        if (isset($tag['index'])) {
            $index = $tag['index'];
            $parseStr .= '$' . $index . '=0; ';
        }
        $parseStr .= 'foreach(' . $var . ' as $' . $key . '=>$' . $item . '): ';
        // 设置了索引项
        if (isset($tag['index'])) {
            $index = $tag['index'];
            if (isset($tag['mod'])) {
                $mod = (int) $tag['mod'];
                $parseStr .= '$mod = ($' . $index . ' % ' . $mod . '); ';
            }
            $parseStr .= '++$' . $index . '; ';
        }
        $parseStr .= '?>';
        // 循环体中的内容
        $parseStr .= $content;
        $parseStr .= '<?php endforeach; endif; else: echo "' . $empty . '" ;endif; ?>';

        if (!empty($parseStr)) {
            return $parseStr;
        }
        return;
    }

    /**
     * if标签解析
     * 格式：
     * {if condition=" $a eq 1"}
     * {elseif condition="$a eq 2" /}
     * {else /}
     * {/if}
     * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || &&
     * @access public
     * @param array $tag 标签属性
     * @param string $content 标签内容
     * @return string
     */
    public function tagIf($tag, $content)
    {
        $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition'];
        $condition = $this->parseCondition($condition);
        $parseStr  = '<?php if(' . $condition . '): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    /**
     * elseif标签解析
     * 格式：见if标签
     * @access public
     * @param array $tag 标签属性
     * @param string $content 标签内容
     * @return string
     */
    public function tagElseif($tag, $content)
    {
        $condition = !empty($tag['expression']) ? $tag['expression'] : $tag['condition'];
        $condition = $this->parseCondition($condition);
        $parseStr  = '<?php elseif(' . $condition . '): ?>';
        return $parseStr;
    }

    /**
     * else标签解析
     * 格式：见if标签
     * @access public
     * @param array $tag 标签属性
     * @return string
     */
    public function tagElse($tag)
    {
        $parseStr = '<?php else: ?>';
        return $parseStr;
    }

    /**
     * switch标签解析
     * 格式：
     * {switch name="a.name"}
     * {case value="1" break="false"}1{/case}
     * {case value="2" }2{/case}
     * {default /}other
     * {/switch}
     * @access public
     * @param array $tag 标签属性
     * @param string $content 标签内容
     * @return string
     */
    public function tagSwitch($tag, $content)
    {
        $name     = !empty($tag['expression']) ? $tag['expression'] : $tag['name'];
        $name     = $this->autoBuildVar($name);
        $parseStr = '<?php switch(' . $name . '): ?>' . $content . '<?php endswitch; ?>';
        return $parseStr;
    }

    /**
     * case标签解析 需要配合switch才有效
     * @access public
     * @param array $tag 标签属性
     * @param string $content 标签内容
     * @return string
     */
    public function tagCase($tag, $content)
    {
        $value = !empty($tag['expression']) ? $tag['expression'] : $tag['value'];
        $flag  = substr($value, 0, 1);
        if ('$' == $flag || ':' == $flag) {
            $value = $this->autoBuildVar($value);
            $value = 'case ' . $value . ':';
        } elseif (strpos($value, '|')) {
            $values = explode('|', $value);
            $value  = '';
            foreach ($values as $val) {
                $value .= 'case "' . addslashes($val) . '":';
            }
        } else {
            $value = 'case "' . $value . '":';
        }
        $parseStr = '<?php ' . $value . ' ?>' . $content;
        $isBreak  = isset($tag['break']) ? $tag['break'] : '';
        if ('' == $isBreak || $isBreak) {
            $parseStr .= '<?php break; ?>';
        }
        return $parseStr;
    }

    /**
     * default标签解析 需要配合switch才有效
     * 使用： {default /}ddfdf
     * @access public
     * @param array $tag 标签属性
     * @param string $content 标签内容
     * @return string
     */
    public function tagDefault($tag)
    {
        $parseStr = '<?php default: ?>';
        return $parseStr;
    }

    /**
     * 解析条件表达式
     * @access public
     * @param  string $condition 表达式标签内容
     * @return string
     */
    public function parseCondition($condition)
    {
        if (strpos($condition, ':')) {
            $condition = ' ' . substr(strstr($condition, ':'), 1);
        }
        $condition = str_ireplace(array_keys($this->comparison), array_values($this->comparison), $condition);
        $this->parseVar($condition);
        // $this->tpl->parseVarFunction($condition); // XXX: 此句能解析表达式中用|分隔的函数，但表达式中如果有|、||这样的逻辑运算就产生了歧异
        return $condition;
    }

    /**
     * 自动识别构建变量
     *
     * @param string $name 变量描述
     * @return string
     */
    public function autoBuildVar(&$name)
    {
        $flag = substr($name, 0, 1);
        if (':' == $flag) {
            // 以:开头为函数调用，解析前去掉:
            $name = substr($name, 1);
        } elseif ('$' != $flag && preg_match('/[a-zA-Z_]/', $flag)) {
            // XXX: 这句的写法可能还需要改进
            // 常量不需要解析
            if (defined($name)) {
                return $name;
            }
            // 不以$开头并且也不是常量，自动补上$前缀
            $name = '$' . $name;
        }

        $this->parseVar($name);
        return $name;
    }
}
